import { user,system } from '@/store';
import * as u from '@/tool/function/util';
import { uuid,encryptSM4 } from '@/common/utils/util';
import systemConfig from '@/common/config';
import commonApi from '@/api/modules/common';
import ImageCompressor from 'image-compressor.js';
import { signFileParams } from '@/common/utils/signatureUtil';
import http from '@/common/utils/http';
//文件上传的状态值
export enum statusCode {
  //待上传
  upload = 0,
  //上传中
  uploading = 1,
  //上传失败
  fail = 2,
  //上传成功
  success = 3,
  //超过大小限制
  max = 4,
}
//文件对象结构
export interface file {
  url: string; //当前显示的图片地址，这是个本地临时地址。待上传前，成功后会替换服务器地址。
  status?: string; //上传状态文本
  progress?: number; //当前文件上传的进度
  uid?: string | number; //文件唯一标识id
  showId: string; //文件服务器id
  tempFilePath: string; //本地临时路径
  remarks: string; //备注说明
  statusCode?: statusCode; //文件状态
  response?: any; //上传成功后的回调数据。
  name?: string; //文件名称
  defaultShow?: Boolean; //是否默认显示
  fileType?: String; //文件类型image/video/file
  [propName: string]: any;
}
//上传class对象的配置。
export interface fileConfig {
  maxSize?: number; //每一个图片文件上传的最大尺寸，默认为10mb
  maxVideoSize?: number; //每一个视频文件上传的最大尺寸，默认为500mb
  count?: number; //一次选择文件最大数量
  maxFile?: number; //总共可选择的文件数量。
  sourceType?: Array<string>; //文件选择的类型。
  hostUrl?: string; //上传文件的服务器地址
  defaultList?: Array<file>; //默认的文件列表。
  fileList?: Array<file>; //已上传的文件列表。
  autoUpload?: Boolean;
  header?: Object; //头部参数。
  formData?: Object; //额外的表单数据。
  formName?: string;
  compressed?: Boolean; //是否压缩
  sizeType?: Array<string>; //original 原图，compressed 压缩图，默认二者都有
  fileType?: Array<string>; //文件类型 image/video/file
  maxDuration?: number; //视频最大录制长度
  fileClassCode?: String; //文件分类
  statusCode?: number; //服务返回成功的状态码标志，默认200表示成功。
}
export function getUid() {
  return uuid();
}
/**
 * 上传文件。
 * 作者：tmzdy
 * 时间：2022年4月27日
 * 联系：zhongjihan@sina.com
 * @method {Function} beforeChooesefile -- 选择图片上传前执行的勾子。
 * @method {Function} chooesefile -- 选择图片上传，弹出层进行选择文件 。
 * @method {Function} chooesefileAfter -- 选择图片、视频文件成功后触发。返回选择后的文件。此时还未加入待上传列表，会返回 一个文件列表用于过滤。需要在函数中再返回 一个文件 列表，
 * @method {Function} chooesefileSuccess -- 文件已经加入到了待上传文件列表,需要返回过滤的文件列表
 * @method {Function} beforeAddfile -- 动态加入预计加入文件前执行的勾子，返回true才会正式加入到待上传列表中。
 * @method {Function} addFile -- 动态加入预上传的文件。
 * @method {Function} progress -- 进度。
 * @method {Function} fail -- 失败。服务器出现非200时触发。
 * @method {Function} beforeSuccess -- 服务器返回成功，立即执行的勾子。如果此时返回false,则表示文件 上传失败。
 * @method {Function} success -- 成功。
 * @method {Function} complete -- 单个文件完成。不管上传成功与失败否，都会触发此函数。
 * @method {Function} uploadComplete -- 所有文件上传完时触发
 * @method {Function} beforeStart -- 开始上传。前的校验勾子。如果返回false,将阻止上传
 * @method {Function} start -- 开始上传。
 * @method {Function} stop -- 停止上传。中止上传时触发的函数。
 */
export class uploadfile {
  //文件列表。
  filelist: Array<file> = [];
  isStop = false;
  index = 0;
  config: fileConfig = {};
  uploadobj: UniNamespace.UploadTask | null = null;
  constructor(config: fileConfig) {
    let cf: fileConfig = {
      maxSize: 10 * 1024 * 1024,
      maxVideoSize: 500 * 1024 * 1024,
      count: 1,
      maxFile: 9,
      sourceType: ['album', 'camera'],
      fileList: [],
      defaultList: [],
      autoUpload: true,
      header: {},
      formData: {},
      formName: 'file',
      compressed: true,
      sizeType: ['original', 'compressed'],
      fileType: ['image'],
      maxDuration: 60,
      fileClassCode: '',
    };
    cf = { ...cf, ...(arguments[0] ?? {}) };
    //配置{name: 'file', // 上传时的文件key名。默认file,header: {}, // 上传的头部参数。}
    this.config = cf;
    this.addFile(cf.fileList);
    delete this.config.fileList;
  }
  async beforeChooesefile() {
    return true;
  }
  async chooesefileAfter(fileList: Array<file>) {
    return fileList;
  }
  async chooesefileSuccess(fileList: Array<file>) {
    return fileList;
  }
  delete(item: file) {
    let index = this.filelist.findIndex((el) => el.uid == item.uid);
    if (index > -1) {
      let p = [...this.filelist];
      if (item.defaultShow) {
        p.splice(index, 1, {
          ...item,
          status: '待上传',
          statusCode: statusCode.upload,
          tempFilePath: '',
          progress: 0,
          name: '',
          response: null,
          url: '',
        });
      } else {
        p.splice(index, 1);
      }

      this.filelist = [...p];
    }

    return this.filelist;
  }
  async clear() {
    /** 清清前要选暂停所有正在上传的文件 */
    this.stop();
    this.filelist = [];
  }
  setFileStatus(item: file) {
    let index = this.filelist.findIndex((el) => el.uid == item.uid);
    if (index > -1) {
      let p = [...this.filelist];
      p.splice(index, 1, item);
      this.filelist = [...p];
    }
  }
  hasTypeCount(keys: string[]): number {
    //包含类型数量
    let t: any = this;
    let count = 0;
    keys.forEach((key) => {
      if (t.config.fileType.findIndex((item: string) => item === key) > -1) count++;
    });
    return count;
  }
  /**
   * 成功后返回选择后的图片列表。
   */
  async chooesefile(defaultFile: file): Promise<Array<file>> {
    let t: any = this;

    return new Promise(async (rs, rj) => {
      let isready = await t.beforeChooesefile();
      if (!isready) {
        rs([]);
        return;
      }
      let defaultShow = false;
      let theCount = t.config.maxFile - this.filelist.length;
      if (defaultFile && defaultFile.defaultShow) {
        theCount = 1;
        defaultShow = true;
      }

      if (theCount <= 0) {
        rj('超过最大可选择数量');
        return;
      }
      if (t.hasTypeCount(['image']) === 1) {
        uni.chooseImage({
          count: theCount > t.config.count ? t.config.count : theCount,
          sizeType: t.config.sizeType,
          sourceType: t.config.sourceType,
          fail: (e) => {
            rj('取消选择');
          },
          success: async (res) => {
            if (res.tempFilePaths.length == 0) {
              rj('未选择');
              return;
            }
            let isreadyChoose = [];
            let imgarray: any = res.tempFilePaths;
            let fielist: any = res.tempFiles;
            if (defaultShow) {
              let index = t.filelist.findIndex((el: file) => el.uid == defaultFile.uid);
              if (index > -1) {
                let isMaxsize = fielist[0].size > t.config.maxSize ? true : false;
                let p = [...this.filelist];
                let path = imgarray[0];
                await this.compress(fielist[0], imgarray[0]).then((res) => {
                  path = res;
                });
                let newItem = {
                  ...defaultFile,
                  url: imgarray[0],
                  tempFilePath: path,
                  status: isMaxsize ? '超过大小' : '待上传',
                  progress: isMaxsize ? 100 : 0,
                  showId: '', //文件服务器id
                  statusCode: isMaxsize ? statusCode.max : statusCode.upload,
                  response: null,
                  fileSize: fielist[0].size,
                  name: fielist[0].name ?? '',
                  fileType: 'image',
                };

                isreadyChoose = await t.chooesefileAfter([newItem]);
                if (!Array.isArray(isreadyChoose) || typeof isreadyChoose != 'object') {
                  rj('chooesefileAfter:函数过滤，没有返回文件列表。');
                  return;
                }
                p.splice(index, 1, isreadyChoose[0]);
                t.filelist = [...p];
              }
            } else {
              let jgsk: Array<file> = [];
              //0待上传，1上传中，2上传失败，3上传成功。4超过大小限制
              for (let index = 0; index < imgarray.length; index++) {
                let item = imgarray[index];
                let isMaxsize = fielist[index].size > t.config.maxSize ? true : false;
                let path = item;
                await this.compress(fielist[index], item).then((res) => {
                  path = res;
                });
                jgsk.push({
                  url: item,
                  status: isMaxsize ? '超过大小' : '待上传',
                  progress: isMaxsize ? 100 : 0,
                  uid: getUid(),
                  showId: '', //文件服务器id
                  tempFilePath: path, //本地临时路径
                  remarks: '', //备注说明
                  statusCode: isMaxsize ? statusCode.max : statusCode.upload,
                  response: null,
                  name: fielist[index].name ?? '',
                  fileType: 'image',
                  fileSize: fielist[index].size,
                });
              }
              isreadyChoose = await t.chooesefileAfter(jgsk);

              if (!Array.isArray(isreadyChoose) || typeof isreadyChoose != 'object') {
                rj('chooesefileAfter:函数过滤，没有返回文件列表。');
                return;
              }

              t.filelist.push(...isreadyChoose);
            }

            t.chooesefileSuccess(isreadyChoose);
            rs(isreadyChoose);
            if (t.config.autoUpload) {
              setTimeout(function () {
                t.start();
              }, 500);
            }
          },
        });
      } else if (t.hasTypeCount(['video']) === 1) {
        uni.chooseVideo({
          sourceType: t.config.sourceType,
          camera: 'back',
          maxDuration: t.config.maxDuration,
          compressed: t.config.compressed,
          fail: (e) => {
            rj('取消选择');
          },
          success: async (res) => {
            if (!res.tempFilePath) {
              rj('未选择');
              return;
            }
            let isMaxsize = false;
            if (res.size) {
              isMaxsize = res.size > t.config.maxVideoSize ? true : false;
            }
            let isreadyChoose = [];
            if (defaultShow) {
              let index = t.filelist.findIndex((el: file) => el.uid == defaultFile.uid);
              if (index > -1) {
                let p = [...this.filelist];
                let newItem = {
                  ...defaultFile,
                  url: res.tempFilePath,
                  tempFilePath: res.tempFilePath,
                  status: isMaxsize ? '超过大小' : '待上传',
                  progress: isMaxsize ? 100 : 0,
                  showId: '', //文件服务器id
                  statusCode: isMaxsize ? statusCode.max : statusCode.upload,
                  response: null,
                  name: res.name ?? '',
                  fileType: 'video',
                };
                p.splice(index, 1, newItem);
                isreadyChoose = await t.chooesefileAfter([newItem]);
                if (!Array.isArray(isreadyChoose) || typeof isreadyChoose != 'object') {
                  rj('chooesefileAfter:函数过滤，没有返回文件列表。');
                  return;
                }
                t.filelist = [...p];
              }
            } else {
              let newItem = {
                url: res.tempFilePath,
                status: isMaxsize ? '超过大小' : '待上传',
                progress: isMaxsize ? 100 : 0,
                uid: getUid(),
                showId: '', //文件服务器id
                tempFilePath: res.tempFilePath, //本地临时路径
                remarks: '', //备注说明
                statusCode: isMaxsize ? statusCode.max : statusCode.upload,
                response: null,
                name: res.name ?? '',
                fileType: 'video',
              };
              isreadyChoose = await t.chooesefileAfter([newItem]);

              if (!Array.isArray(isreadyChoose) || typeof isreadyChoose != 'object') {
                rj('chooesefileAfter:函数过滤，没有返回文件列表。');
                return;
              }
              t.filelist.push(...isreadyChoose);
            }
            t.chooesefileSuccess(isreadyChoose);
            rs(isreadyChoose);
            if (t.config.autoUpload) {
              setTimeout(function () {
                t.start();
              }, 500);
            }
          },
        });
      } else {
        rj('暂不支持选择图片和视频之外的其他类型文件');
      }
    });
  }
  async compress(file: any, filePath: string) {
    //如果是视频，直接返回
    if (file.type.indexOf('video') > -1) {
      return filePath;
    }
    return new Promise<string>(async (resolve, reject) => {
      if (file.size <= 1024 * 100) {
        resolve(filePath);
        return filePath;
      }
      console.log('Image size:' + file.size);
      //#ifdef H5
      //压缩图片
      let options: any = {
        quality: 0.6, //压缩质量
        checkOrientation: false, //图片翻转
        convertSize: 2 * 1024 * 1024,
        // Compression success callback
        success: async function (result: any) {
          resolve(window.URL.createObjectURL(result));

          // console.log('result:', result)
          console.log('Image size after compression:', result.size);
          console.log('mime type:', result.type);
          // console.log('Actual compression ratio:', ((file.size-result.size) / file.size * 100).toFixed(2) +'%');
        },
      };
      await new ImageCompressor().compress(file, options);
      // #endif
      //#ifndef H5
      await uni.compressImage({
        src: filePath,
        quality: 60,
        success: async (res) => {
          resolve(res.tempFilePath);
        },
      });
      // #endif
    });
  }
  setConfig(config: fileConfig) {
    this.config = { ...this.config, ...(config ?? {}) };
  }
  /**
   * 动态加入文件
   * @param {Object} filelist
   */
  addFile(filelist: Array<file> = []) {
    if (typeof filelist !== 'object' && !Array.isArray(filelist)) return;
    let total_uid = new Set(this.filelist.map((e) => e.uid));
    let total_url = new Set(this.filelist.map((e) => e.url));
    let defaultList: any = this.config.defaultList;
    let defSize = defaultList?.length || 0;
    let fileSize = filelist?.length || 0;
    let cfilelist: Array<file> = [];
    let fileType = 'file';
    if (this.hasTypeCount(['image']) === 1) {
      fileType = 'image';
    } else if (this.hasTypeCount(['video']) === 1) {
      fileType = 'video';
    }
    if (fileSize === 0 && defSize > 0) {
      cfilelist = defaultList.map((el: file) => {
        return {
          ...el,
          status: el?.status ?? '待上传',
          statusCode: el?.statusCode ?? statusCode.upload,
          uid: el?.uid ?? getUid(),
          progress: el?.progress ?? 0,
          name: el?.name ?? '',
          response: el?.response ?? null,
          url: el?.url ?? '',
          fileType: fileType,
        };
      });
    } else if (fileSize > 0 && defSize === 0) {
      cfilelist = filelist.map((el) => {
        return {
          ...el,
          status: el?.status ?? '待上传',
          statusCode: el?.statusCode ?? statusCode.upload,
          uid: el?.uid ?? getUid(),
          progress: el?.progress ?? 0,
          name: el?.name ?? '',
          response: el?.response ?? null,
          url: el?.url ?? '',
          defaultShow: false,
          fileType: fileType,
        };
      });
    } else {
      if (fileSize >= defSize) {
        let i = 0;
        cfilelist = filelist.map((el) => {
          let defFile = { remarks: '', defaultShow: false };
          if (i <= defSize) {
            defFile = defaultList[i];
          }
          i++;
          return {
            ...el,
            status: el?.status ?? '待上传',
            statusCode: el?.statusCode ?? statusCode.upload,
            uid: el?.uid ?? getUid(),
            progress: el?.progress ?? 0,
            name: el?.name ?? '',
            response: el?.response ?? null,
            url: el?.url ?? '',
            remarks: defFile?.remarks ?? '',
            defaultShow: defFile?.defaultShow ?? false,
            fileType: fileType,
          };
        });
      } else {
        let i = 0;
        cfilelist = filelist.map((el) => {
          let defFile = { remarks: '', defaultShow: false };
          if (i <= defSize) {
            defFile = defaultList[i];
          }
          i++;
          return {
            ...el,
            status: el?.status ?? '待上传',
            statusCode: el?.statusCode ?? statusCode.upload,
            uid: el?.uid ?? getUid(),
            progress: el?.progress ?? 0,
            name: el?.name ?? '',
            response: el?.response ?? null,
            url: el?.url ?? '',
            remarks: defFile?.remarks ?? '',
            defaultShow: defFile?.defaultShow ?? false,
            fileType: fileType,
          };
        });
        for (let j = i; j < defSize; j++) {
          let defFile = defaultList[j];
          cfilelist.push({
            ...defFile,
            status: defFile?.status ?? '待上传',
            statusCode: defFile?.statusCode ?? statusCode.upload,
            uid: defFile?.uid ?? getUid(),
            progress: defFile?.progress ?? 0,
            name: defFile?.name ?? '',
            response: defFile?.response ?? null,
            url: defFile?.url ?? '',
            fileType: fileType,
          });
        }
      }
    }
    let filterFIle = cfilelist.filter((item) => !total_uid.has(item.uid) && !total_url.has(item.url));
    this.filelist.push(...filterFIle);
  }
  beforeSuccess(item: file) {
    return Promise.resolve(true);
  }
  beforeStart(item: file) {
    return Promise.resolve(true);
  }
  // 进度。
  progress(item: file) {}
  // 失败
  fail(item: file) {}
  // 成功
  success(item: file, fileList: Array<file>) {}
  // 完成。
  complete(filelist: file) {}
  uploadComplete(filelist: Array<file>) {}
  awaitTime() {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(true);
      }, 20);
    });
  }
  // 开始上传。
  async start() {
    if (this.filelist.length <= 0) {
      console.error('未选择图片,已取消上传');
      return;
    }
    this.index = 0;
    let t = this;
    this.isStop = false;
    async function startupload() {
      if (t.isStop) return;

      let item = t.filelist[t.index];

      if (!item || typeof item === 'undefined') {
        // 文件不存在。直接结束。
        t.uploadComplete(t.filelist);
        return;
      }
      if (!item.tempFilePath) {
        // 文件不存在。直接结束。
        t.index++;
        startupload();
        return;
      }
      let canbleStart = await t.beforeStart(item);
      if (!canbleStart) {
        item.statusCode = statusCode.fail;
        item.status = '不允许上传';
        t.filelist.splice(t.index, 1, item);
        t.index++;
        t.setFileStatus(item);
        t.fail(item);
        t.complete(item);
        startupload();
        return;
      }

      if (item.statusCode == 3 || item.statusCode == 1 || item.statusCode == 4 || item.statusCode == 2) {
        // 直接跳过。至下一个文件。
        t.index++;
        startupload();
        return;
      }

      item.statusCode = statusCode.uploading;
      item.status = '上传中...';
      t.setFileStatus(item);
      let token = user.accesstoken;
      if(!token){
        token=u.getCookie(systemConfig.localTokenName);
      }
      
      let formData = {
        fileName: item.name?item.name:'',
        ...t.config.formData,
        uploaderId: user.userId?user.userId:'',
        uploader: user.fullName?user.fullName:'',
        fileClassCode: t.config?.fileClassCode,
      };
      const {signature, timestamp} =signFileParams()
      let header: any = t.config?.header ?? {};
      if (token) {
        header[systemConfig.accessTokenName] = token;
      }
      header.appId=systemConfig.appId;
      header.signature=signature;
      header.timestamp=timestamp+'';
      const upObj = (t.uploadobj = uni.uploadFile({
        url: String(t.config.hostUrl),
        name: t.config?.formName ?? 'file',
        header: header,
        filePath: item.tempFilePath,
        formData: {fileInfo:JSON.stringify(formData)},
        success: async (res: any) => {
          if (t.isStop) return;
          const data = JSON.parse(res.data);
          item.response = data;
          let isOksuccess = await t.beforeSuccess(item);
          const statusCode_reonese = t.config?.statusCode ?? 200;
          if (res.statusCode != statusCode_reonese || !isOksuccess) {
            item.statusCode = statusCode.fail;
            item.status = '上传失败';
            t.fail(item);
            t.setFileStatus(item);
            t.index++;
            return;
          }
          if (data && data.code !== 0) {
            item.statusCode = statusCode.fail;
            item.status = '上传失败';
            t.fail(item);
            t.setFileStatus(item);
            t.index++;
            t.handleError(data)
            return;
          }
          const file = data.result.file;
          if (!file.flag) {
            item.statusCode = statusCode.fail;
            item.status = '上传失败';
            t.fail(item);
            t.setFileStatus(item);
            t.index++;
            return;
          }
          // 上传成功。
          item.statusCode = statusCode.success;
          item.status = '上传成功';
          item.showId = file.showId;
          item.url = commonApi.fileShowUrl.url + file.showId;
          item.progress = 100;
          if (!item.name) {
            item.name = file.fileName;
          }
          t.setFileStatus(item);
          t.success(item, t.filelist);
          t.index++;
        },
        fail: (res:any) => {
          if (t.isStop) return;
          item.statusCode = statusCode.fail;
          item.status = '上传失败';
          t.setFileStatus(item);
          t.fail(item);
          t.index++;
          t.handleError(res)
        },
        complete: async (res) => {
          if (t.isStop) return;
          await t.awaitTime();
          t.complete(item);
          // 直接下一个文件。
          startupload();
        },
      }));
      if (upObj) {
        let item = t.filelist[t.index];
        upObj.onProgressUpdate(async (res) => {
          if (t.isStop) return;
          item.progress = res.progress;
          item.statusCode = statusCode.uploading;
          item.status = '上传中...';
          t.setFileStatus(item);
          t.progress(item);
        });
      }
    }
    await startupload();
  }
  // 停止上传
  stop() {
    this.isStop = true;
    if (this.uploadobj != null) {
      this.uploadobj.abort();
    }
  }
  handleError(res:any){
    if(res.code<0){
      let errMsg=res.message;
      uni.showToast({
        title: errMsg || '上传异常！',
        icon: 'none',
        duration: 4000,
      });
    }else if(res.code===456){
      let refreshToken=user.refreshToken;
      if(!refreshToken){
          refreshToken=u.getCookie(systemConfig.localRefreshTokenName);
      }
      if(!refreshToken){
        uni.showModal({
            title: '未登录',
            content: '当前用户已登出，请尝试重新登录后再操作。',
            confirmText: '重新登录',
            success: () => {
              user.setToken(null,null);
              uni.reLaunch({ url: systemConfig.loginPath });
            },
        });  
      }else{
        user.setToken(null,refreshToken)
        let config: any = {};
        let headers = { 'func-code': 'base0018',cryptoEnabled:'true' };
        config.header = headers;
        let params={} as any;
        params[systemConfig.refreshTokenName]=user.refreshToken;
        let url=systemConfig.fileUrl+systemConfig.commonUrl + 'base0018'
        http.post(url, params,config).then((res:any) => {
            const result = res.result;
            user.setToken(result.accessToken,result.refreshToken)
            if(result.secretKey){
                u.setCookie('USERSECRET',encryptSM4(JSON.stringify(result.secretKey),system.defaultSM4Key));
            }
            user.setUserSecret(result.secretKey);
        }).catch(()=>{
            uni.showModal({
                title: '未登录',
                content: '当前用户已登出，请尝试重新登录后再操作。',
                confirmText: '重新登录',
                success: () => {
                user.setToken(null,null);
                uni.reLaunch({ url: systemConfig.loginPath });
                },
            });
        })
      }
      
    }
  }
}
